/**
 * ReportGear(2011)
 */
package com.reportgear.report.view.grid;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.Rectangle2D;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JToolTip;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.MouseInputAdapter;

import com.reportgear.core.swing.pane.base.JBaseDialog;
import com.reportgear.report.core.model.util.BaseUtils;
import com.reportgear.report.model.auxiliary.ColumnRow;
import com.reportgear.report.model.auxiliary.RowPropList;
import com.reportgear.report.model.auxiliary.RowType;
import com.reportgear.report.view.JWorksheet;
import com.reportgear.report.view.cursor.CursorUtils;
import com.reportgear.report.view.grid.pane.NumberInputPane;
import com.reportgear.report.view.grid.selection.GridSelection;
import com.reportgear.report.view.grid.selection.Selection;
import com.reportgear.report.view.grid.support.DefaultGridRowCRRenderer;
import com.reportgear.report.view.grid.support.GridCRRenderer;
import com.reportgear.report.view.grid.util.GraphHelper;
import com.reportgear.report.view.util.WorksheetUtils;

/**
 * Grid行头
 * 
 * @version 1.0 2011-3-14
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since Report 1.0
 * 
 */
public class GridRowHeader extends BaseGridComponent {
	/**
	 * 鼠标事件处理
	 */
	public class GridRowHeaderMouseHandler extends MouseInputAdapter {

		// 拖动行Index
		private int dragRowIndex = 0;
		// 拖动类型
		private DragType dragType = DragType.DRAG_NONE;
		// 多行拖动结束Index
		private int endMultiSelectIndex = 0;
		// 支持拖动改变大小
		private boolean isDragPermited = false;
		// 多行多动选择
		private boolean isMultiRowsSelectDragPermited = false;
		// 行提示
		private JToolTip rowTip = null;
		// 多行拖动开始Index
		private int startMultiSelectIndex = 0;
		// 提示窗体
		private JWindow tipWindow = null;

		public GridRowHeaderMouseHandler() {
		}

		private String createToolTipString(double h, double th) {
			return "高度: " + h + "/" + th + "像素";
		}

		private String createToolTipString(int index) {
			return index + "R";
		}

		/**
		 * 选择行
		 * 
		 * @param row
		 *            行
		 */
		private void doSelectRow(int row) {
			final GridSelection gridSel = worksheet.getGridSelection();
			gridSel.setType(GridSelection.Type.CELL);
			this.startMultiSelectIndex = this.endMultiSelectIndex = row;
			gridSel.setEditBounds(0, row, 1, 1);
			gridSel.setOnlySelection(0, row, worksheet.getColumnCount(), 1);

			worksheet.fireGridSelectionChanged();
		}

		// 拖动多行
		private void dragMultiRows(final GridSelection gridSel, final int xPos, final int yPos) {
			if (!this.isMultiRowsSelectDragPermited) {
				return;
			}

			final ColumnRow cr = WorksheetUtils.getColumnRowByXY(worksheet, xPos, yPos);

			this.endMultiSelectIndex = cr.getRow();
			final int rowSpan = Math.abs(this.endMultiSelectIndex - this.startMultiSelectIndex) + 1;
			final int startIndex = Math.min(this.startMultiSelectIndex, this.endMultiSelectIndex);

			gridSel.setEditBounds(0, startIndex, 1, 1);
			gridSel.setOnlySelection(0, startIndex, worksheet.getColumnCount(), rowSpan);

			this.setToolTipText2(this.createToolTipString(rowSpan));

			worksheet.fireGridSelectionChanged();
		}

		// 拖动单行
		private void dragSingleRow(final int yPos) {
			if (!this.isDragPermited) {
				return;
			}

			final RowPropList rpl = reportModel.getRowPropList();

			final int h = yPos - rpl.getRangeValue(worksheet.getVertBeginValue(), this.dragRowIndex);
			rpl.setValue(this.dragRowIndex, h);

			this.setToolTipText2(this.createToolTipString(rpl.getValue(this.dragRowIndex), rpl.getRangeValue(0,
					this.dragRowIndex + 1)));
		}

		private void hideToolTip() {
			if (this.tipWindow != null) {
				this.tipWindow.setVisible(false);
			}
		}

		// 是否可编辑
		private boolean isEditable() {
			return isEnabled() ;
		}

		private boolean isOnNormalSeparatorLine(double value, double refValue) {
			return Math.abs(value - refValue) < 2D;
		}

		@Override
		public void mouseDragged(MouseEvent evt) {
			if (!this.isEditable()) {
				return;
			}

			if (this.dragType == DragType.DRAG_NONE || SwingUtilities.isRightMouseButton(evt)) {
				return;
			}

			worksheet.stopEditing();

			final GridSelection gridSel = worksheet.getGridSelection();
			gridSel.setType(GridSelection.Type.CELL);

			if (this.dragType == DragType.DRAG_SELECTED_ROWS) {// 多行选择
				this.dragMultiRows(gridSel, evt.getX(), evt.getY());
			} else {// 单行选择
				this.dragSingleRow(evt.getY());
			}

			worksheet.repaint();
		}

		@Override
		public void mouseMoved(MouseEvent evt) {
			if (!this.isEditable()) {
				return;
			}

			setCursor(CursorUtils.CURSOR_V_SELECT);

			final RowPropList rpl = reportModel.getRowPropList();

			double rowHeight = 0.0D;
			double tmpTotalHeight = 0.0D;

			final double height = getHeight();
			int pointer = worksheet.getVertBeginValue();

			final double yPos = evt.getY();
			while (tmpTotalHeight < height) {
				rowHeight = rpl.getValue(pointer);

				if (rowHeight <= 0.0D) {
					rowHeight = 1.0D;
				}

				tmpTotalHeight += rowHeight;

				if (this.isOnNormalSeparatorLine(yPos, tmpTotalHeight)) {
					setCursor(CursorUtils.CURSOR_V_MOVE);
					break;
				}

				pointer++;
			}
		}

		@Override
		public void mousePressed(MouseEvent evt) {
			if (!this.isEditable()) {
				return;
			}

			worksheet.stopEditing();
			final int xPos = evt.getX();
			final int yPos = evt.getY();
			final RowPropList rpl = reportModel.getRowPropList();
			double rowHeight = 0.0D;
			double tmpTotalHeight = 0.0D;
			final double height = getSize().getHeight();

			int pointer = worksheet.getVertBeginValue();
			while (tmpTotalHeight < height) {
				rowHeight = rpl.getValue(pointer);

				if (rowHeight <= 0.0D) {
					rowHeight = 1.0D;
				}

				tmpTotalHeight += rowHeight;

				if (this.isOnNormalSeparatorLine(yPos, tmpTotalHeight)) {
					this.dragType = DragType.DRAG_HEIGHT;
					this.isDragPermited = true;
					this.dragRowIndex = pointer;
					final MouseEvent event = evt;
					SwingUtilities.invokeLater(new Runnable() {

						public void run() {
							showToolTip(event, GridRowHeaderMouseHandler.this.createToolTipString(rpl
									.getValue(dragRowIndex), rpl.getRangeValue(0, dragRowIndex + 1)));
						}
					});

					break;
				}

				if (yPos > (tmpTotalHeight - rowHeight) && yPos <= tmpTotalHeight) {
					this.dragType = DragType.DRAG_SELECTED_ROWS;
					this.isMultiRowsSelectDragPermited = true;
					this.startMultiSelectIndex = pointer;
					this.showToolTip(evt, this.createToolTipString(this.startMultiSelectIndex + 1));

					break;
				}

				pointer++;
			}

			if (evt.getClickCount() > 1) {// 双击
				if (popupMenu == null) {
					popupMenu = new GridRowPopupMenu();
				}
				this.hideToolTip();
			}

			if (SwingUtilities.isRightMouseButton(evt)) {// 右键
				if (popupMenu == null) {
					popupMenu = new GridRowPopupMenu();
				}
				this.hideToolTip();
				popupMenu.show(GridRowHeader.this, xPos, yPos);
			} else if (this.dragType == DragType.DRAG_SELECTED_ROWS) {
				worksheet.getGridSelection().setType(GridSelection.Type.CELL);

				final ColumnRow columnRow = WorksheetUtils.getColumnRowByXY(worksheet, xPos, yPos);
				this.doSelectRow(columnRow.getRow());
			}

			worksheet.refresh();
		}

		@Override
		public void mouseReleased(MouseEvent evt) {
			if (!this.isEditable()) {
				return;
			}

			if (this.dragType == DragType.DRAG_NONE || SwingUtilities.isRightMouseButton(evt)) {
				return;
			}

			worksheet.stopEditing();
			this.hideToolTip();

			final GridSelection gridSel = worksheet.getGridSelection();
			final Selection selection = gridSel.getLastCellSelection();
			final RowPropList rpl = reportModel.getRowPropList();

			// 修改所有选择的行的高度
			if (this.dragType == DragType.DRAG_HEIGHT) {
				final int row = selection.getRow();
				final int h = selection.getRowSpan();
				if ((this.dragRowIndex - row < h) && (this.dragRowIndex >= row)) {
					for (int i = 0; i < h; i++) {
						rpl.setValue(row + i, rpl.getValue(this.dragRowIndex));
					}
				}
				worksheet.fireReportDataChanged();
			}

			this.isMultiRowsSelectDragPermited = false;
			this.isDragPermited = false;
			this.dragType = DragType.DRAG_NONE;
			this.dragRowIndex = 0;

			worksheet.refresh();
		}

		private void setToolTipText2(String s) {
			if (this.rowTip == null) {
				return;
			} else {
				this.rowTip.setTipText(s);
				this.rowTip.setSize(this.rowTip.getPreferredSize());
				this.tipWindow.pack();
				this.tipWindow.repaint();
				return;
			}
		}

		private void showToolTip(MouseEvent event, String tips) {
			if (this.tipWindow == null) {
				this.tipWindow = new JWindow();
				this.rowTip = createToolTip();
				this.tipWindow.getContentPane().add(this.rowTip, BorderLayout.CENTER);
			}
			this.rowTip.setTipText(tips);
			final Point p = new Point(0, event.getY());
			SwingUtilities.convertPointToScreen(p, GridRowHeader.this);
			final Dimension dim = this.rowTip.getPreferredSize();
			final int i = p.x + getSize().width + 2;
			final int j = p.y - dim.height / 2;

			this.tipWindow.setLocation(i, j);
			this.tipWindow.pack();
			this.tipWindow.setVisible(true);
		}
	}

	/**
	 * 弹出菜单
	 */
	private class GridRowPopupMenu extends JPopupMenu {

		public GridRowPopupMenu() {
			this.add(new JMenuItem(new AddRowAction()));
			this.add(new JMenuItem(new DeleteRowAction()));
			this.addSeparator();
			this.add(new JMenuItem(new SetRowHeightAction()));
			this.addSeparator();
			this.add(new JMenuItem(new SetRepeatRowHeaderAction()));
			this.add(new JMenuItem(new SetRepeatRowFooterAction()));
			this.add(new JMenuItem(new CancelRepeatRowAction()));
		}

		private class AddRowAction extends AbstractAction {

			public AddRowAction() {
				super("插入行");
			}

			public void actionPerformed(ActionEvent e) {
				Selection selection = worksheet.getLastCellSelection();

				worksheet.getReportModel().addRow(selection.getRow());
				worksheet.refresh();
			}
		}

		/**
		 * 删除行
		 */
		private class DeleteRowAction extends AbstractAction {

			public DeleteRowAction() {
				super("删除行");
			}

			public void actionPerformed(ActionEvent e) {
				Selection selection = worksheet.getLastCellSelection();

				int row = selection.getRow();
				int rowCount = selection.getRowSpan();

				for (int i = rowCount - 1; i >= 0; i--) {
					reportModel.removeRow(row + i);
				}

				worksheet.refresh();
			}
		}

		private class SetRowHeightAction extends AbstractAction {

			public SetRowHeightAction() {
				super("设置行高");
			}

			public void actionPerformed(ActionEvent e) {
				Selection selection = worksheet.getLastCellSelection();

				int row = selection.getRow();
				int rowCount = selection.getRowSpan();

				NumberInputPane inputPane = new NumberInputPane();
				inputPane.setTitle("高度");
				inputPane.populate(reportModel.getRowHeight(row));

				if (inputPane.showDialog(null) == JBaseDialog.RESULT_OK) {
					int value = (int) inputPane.update();
					for (int i = 0; i < rowCount; i++) {
						reportModel.setRowHeight(row + i, value);
					}

					worksheet.refresh();
				}
			}
		}

		private class SetRepeatRowHeaderAction extends AbstractAction {

			public SetRepeatRowHeaderAction() {
				super("设置重复标题行");
			}

			public void actionPerformed(ActionEvent e) {
				Selection selection = worksheet.getLastCellSelection();

				int row = selection.getRow();
				int rowCount = selection.getRowSpan();

				for (int i = 0; i < rowCount; i++) {
					int index = row + i;

					RowType type = reportModel.getRowType(index);

					reportModel.setRowType(index, RowType.HEADER);
				}

				worksheet.refresh();
			}
		}

		private class SetRepeatRowFooterAction extends AbstractAction {

			public SetRepeatRowFooterAction() {
				super("设置重复结尾行");
			}

			public void actionPerformed(ActionEvent e) {
				Selection selection = worksheet.getLastCellSelection();

				int row = selection.getRow();
				int rowCount = selection.getRowSpan();

				for (int i = 0; i < rowCount; i++) {
					int index = row + i;

					RowType type = reportModel.getRowType(index);

					reportModel.setRowType(index, RowType.FOOTER);
				}

				worksheet.refresh();
			}
		}

		private class CancelRepeatRowAction extends AbstractAction {

			public CancelRepeatRowAction() {
				super("取消重复行列");
			}

			public void actionPerformed(ActionEvent e) {
				Selection selection = worksheet.getLastCellSelection();

				int row = selection.getRow();
				int rowCount = selection.getRowSpan();

				for (int i = 0; i < rowCount; i++) {
					int index = row + i;

					reportModel.setRowType(index, RowType.NORMAL);
				}

				worksheet.refresh();
			}
		}
	}

	/**
	 * UID
	 */
	private static final long serialVersionUID = -7296772272428555142L;

	// 单元格Render
	private GridCRRenderer gridCRRender;
	// 弹出菜单
	private GridRowPopupMenu popupMenu;
	// 选中背景色
	private Color selectedBackground = new Color(253, 216, 153);
	// 选中前景色
	private Color selectedForeground = Color.BLACK;
	// 分隔线颜色
	private Color separatorLineColor = new Color(172, 168, 153);
	// 临时矩形对象
	private final Rectangle2D tmpRect = new Rectangle2D.Double(0, 0, 0, 0);

	/**
	 * 构造函数
	 */
	public GridRowHeader(JWorksheet workSheet) {
		super(workSheet);

		final GridRowHeaderMouseHandler mouseHandler = new GridRowHeaderMouseHandler();
		this.addMouseListener(mouseHandler);
		this.addMouseMotionListener(mouseHandler);
		this.gridCRRender = new DefaultGridRowCRRenderer(this.reportModel);
		this.getInputMap().clear();
		this.getActionMap().clear();
		this.setFocusable(false);
		this.setOpaque(true);
	}

	/**
	 * 获取标题绘制对象
	 * 
	 * @return 标题绘制对象
	 */
	public GridCRRenderer getGridCRRender() {
		return this.gridCRRender;
	}

	@Override
	public Dimension getPreferredSize() {
		if (!this.worksheet.isRowHeaderVisible()) {
			return new Dimension(0, 0);
		} else {
			return new Dimension(10 * this.getFontMetrics(this.getFont()).charWidth('M'),
					super.getPreferredSize().height);
		}
	}

	/**
	 * 获取选中背景颜色
	 * 
	 * @return 背景颜色
	 */
	public Color getSelectedBackground() {
		return this.selectedBackground;
	}

	/**
	 * 获取选中前景色
	 * 
	 * @return 选中前景色
	 */
	public Color getSelectedForeground() {
		return this.selectedForeground;
	}

	/**
	 * 获取分隔线颜色
	 * 
	 * @return 分隔线颜色
	 */
	public Color getSeparatorLineColor() {
		return this.separatorLineColor;
	}

	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);

		final Graphics2D g2 = (Graphics2D) g;
		final Dimension dim = this.getSize();

		g2.setPaint(this.getBackground());
		GraphHelper.fill(g2, this.tmpRect);
		this.tmpRect.setRect(0.0D, 0.0D, dim.getWidth(), dim.getHeight());

		final FontRenderContext frContext = g2.getFontRenderContext();
		final LineMetrics lm = this.getFont().getLineMetrics("A", frContext);
		final float ascent = lm.getAscent();

		final RowPropList rpl = this.reportModel.getRowPropList();

		g2.setPaint(this.getSeparatorLineColor());
		GraphHelper.drawLine(g2, 0.0D, 0.0D, dim.getWidth(), 0.0D);
		final GridSelection gridSel = this.worksheet.getGridSelection();

		int beginIndex = this.worksheet.getVertBeginValue();
		int rowExtend = 0;

		double tmpHeight1 = 0.0D;
		double tmpHeight2 = 0.0D;
		double rowHeight = 0.0D;
		final double clientHeight = dim.getHeight();

		final List<Integer> selectedIndexList = BaseUtils.getGridSelectionRowIndex(gridSel);
		boolean tmpSelectStatus;
		while (tmpHeight2 < clientHeight) {
			tmpSelectStatus = false;
			tmpHeight1 += rowHeight;
			rowHeight = rpl.getValue(beginIndex);
			if (rowHeight == 0.0D) {
				tmpHeight2 = tmpHeight1 + 1.0D;
			} else {
				tmpHeight2 = tmpHeight1 + rowHeight;
			}

			if (selectedIndexList.contains(beginIndex)) {
				g2.setPaint(this.getSelectedBackground());
				GraphHelper.fill(g2, new Rectangle2D.Double(0.0D, tmpHeight1 + 1.0D, dim.width, rowHeight - 1.0D));
				tmpSelectStatus = true;
			} else {
				tmpSelectStatus = false;
			}

			g2.setColor(this.getSeparatorLineColor());
			GraphHelper.drawLine(g2, 0.0D, tmpHeight2, dim.getWidth(), tmpHeight2);

			final Object render = this.getGridCRRender().getDisplayInfo(beginIndex);

			if ((render instanceof String) || (render instanceof Number)) {
				final String str = render.toString() + rpl.getTypeStringShort(beginIndex);

				final double strWidth = this.getFont().getStringBounds(str, frContext).getWidth();
				if (tmpSelectStatus) {
					g2.setColor(this.getSelectedForeground());
				} else if (this.isEnabled()) {
					g2.setColor(this.getForeground());
				} else {
					g2.setPaint(UIManager.getColor("controlShadow"));
				}
				GraphHelper.drawString(g2, str, (dim.width - strWidth) / 2.0D, tmpHeight1 + 2.0D + ascent);
			}

			beginIndex++;
			rowExtend++;
		}

		// 设置视图显示行数
		this.worksheet.setVertValueExtend(rowExtend);

		g2.setColor(this.getSeparatorLineColor());
		GraphHelper.drawLine(g2, 0.0D, 0.0D, 0.0D, clientHeight);
	}

	/**
	 * 设置标题绘制对象
	 * 
	 * @param render
	 *            绘制对象
	 */
	public void setGridCRRender(GridCRRenderer render) {
		this.gridCRRender = render;
	}

	/**
	 * 设置选中背景颜色
	 * 
	 * @param newColor
	 *            新颜色
	 */
	public void setSelectedBackground(Color newColor) {
		this.selectedBackground = newColor;
		this.repaint();
	}

	/**
	 * 设置选中前景色
	 * 
	 * @param newColor
	 *            新颜色
	 */
	public void setSelectedForeground(Color newColor) {
		this.selectedForeground = newColor;
		this.repaint();
	}

	/**
	 * 设置分隔线颜色
	 * 
	 * @param newColor
	 *            新颜色
	 */
	public void setSeparatorLineColor(Color newColor) {
		this.separatorLineColor = newColor;
		this.repaint();
	}
}
